- Memory Allocation Types
- ARC - Automatic Reference Counting
- Method Dispatch
- Copy-on-write Pattern
- References
- The stack is static memory allocation.
- Objects allocated on the stack have their memory allocated at compile time.
- Amount of memory required can be calculated at compile time.
- The stack deals with value types:
- Basic value types: structs, arrays, dictionaries, enums, tuples, strings, ints, bools, ...
- Exception 1: a value type as a class property:
- If a value type is a property of a class, then it's stored on the heap.
- Exception 2: a value type captured by a closure:
- If a value type is captured in a closure, then that value will be copied to the heap so that it's still available by the time the closure is executed.
- The stack is efficient for inserting and removing items.
- Time complexity of
O(1)for insertion, deletion, and peak operations. - The cost of allocation and deallocation is low.
- Time complexity of
- The stack is LIFO (Last In First Out) data structure.
- Each thread has its own stack.
- This means objects within it are thread-safe.
- No other thread can access that stack, ensuring the safety of value types within its enclosure context.
- Global access is not possible.
- This means objects within it are thread-safe.
- The heap is dynamic memory allocation.
- Objects allocated on the heap have their memory allocated at run time.
- The heap deals with reference types:
- Classes, actors and closures are stored in the heap.
- Swift utilizes ARC to automatically clean up memory and remove unused objects.
- Disadvantages of using the heap:
- More complex data structure than a stack and slower than a stack.
- Time complexity: Insertion and deletion are in
O(log n), and peak (min/max) isO(1)if you're using a min/max heap.
- Time complexity: Insertion and deletion are in
- Classes can cause memory leaks.
- Thread unsafety: The heap is global memory.
- This can lead to thread unsafety when multiple threads access heap objects simultaneously.
- More complex data structure than a stack and slower than a stack.
-
Swift guarantee that:
- The value type will always have the value type features
- The reference type will always have reference type features
-
Swift does not guarantee that:
- The exact place in memory (heap vs stack).
- The struct will be stored in the stack.
- The class property will be stored in the heap
- The struct will be stored in the heap with all the other fields of the class.
- The closure with all captured value types will be stored in the heap.
- What is ARC?
- ARC is a memory management feature of reference types.
- The goal of ARC is to know which instances (e.g. an instance of a class) can be removed from memory, to free up space.
- ARC only applies to reference types.
- What are benefits of using ARC?
- Helps to avoid memory leaks.
- You can focus on writing code, not on the manual memory management.
- When does ARC operate?
- ARC operates during compile time.
- This contrasts with a garbage collector, which manages memory at runtime.
- How does ARC work?
- Every object in Swift has a property called the retain count.
- It represents the number of owners for a particular object.
- When the retain count is greater than zero, the object is kept in memory.
- When the retain count reaches zero, the object is removed from memory.
- Every object in Swift has a property called the retain count.
Class vs Instance vs Object vs Reference
- Class: structure, a "template" that is used to create objects; a blueprint for a house design.
- Object: all the houses built from that blueprint are objects of that class.
- Instance: a given house is an instance.
- Reference: an address of an instance.
- A retain cycle is if two class instances hold a strong reference to each other, such that each instance keeps the other alive in memory.
- It is common issue:
- When dealing with parent-child relationships between classes when two class instance properties hold a strong reference to each other.
- When you assign an escaping closure to a property of a class instance, and the body of that closure captures
self.
To avoid retain cycles:
- In parent-child relationships between classes use:
- Weak references (
weakkeywords)- Doesn't keep a strong hold on the instance if refers to.
- Are optional and become
nilwhen the instance that it refers to is deallocated.
- Unowned references (
unownedkeywords).- Doesn't keep a strong hold on the instance if refers to.
- Are not optional and are always considered to have a non-
nil. - The other instance has a longer life time, the instance cannot become
nil. - Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated.
- If you try to access the value of an unowned reference after that instance has been deallocated, you'll get a runtime error.
- In closures:
- Use closure capture list.
Method Dispatch is an algorithm that finds the invoked method in memory before it gets executed. there are three types of Method Dispatch:
- Static Dispatch (Direct Dispatch).
- Dynamic Dispatch (Table Dispatch).
- Message Dispatch.
There are few ways to reduce Dynamic Dispatch (using final and private keywords, enabling Whole Module Optimization).
More about Method Dispatch you can find here.
- The Copy-on-write Pattern is used with value types (e.g. structs, arrays, dictionaries, enums and tuples).
- Every time you use the value type in your code, it is potentially a new value type in memory.
- Swift copy that new value type to a new memory address only if you modify it.
- It called copy-on-write pattern. It's a memory optimization.